﻿// Copyright (c) 2007-2021 西安交通信息投资营运有限公司 版权所有
// 作者:吕艳阳

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using DevExpress.XtraBars.Navigation;
using DevExpress.XtraEditors;
using DevExpress.XtraLayout;
using DevExpress.XtraTab;
using XCI.Extensions;
using XCI.Helper;

namespace XCI.Core
{
    public class ContainerData
    {
        /// <summary>
        /// 循环容器中的所有控件及所有子孙控件
        /// </summary>
        /// <param name="container">容器控件</param>
        /// <param name="callback">每个控件回调函数</param>
        public virtual void ForEach(object container, Action<object> callback)
        {
            if (container == null) return;
            if (container is Control containerControl)
            {
                foreach (Control control in containerControl.Controls)
                {
                    callback?.Invoke(control);

                    if (control is LayoutControl)
                    {
                        ForEach(control, callback);
                    }
                    else if (control is SplitContainer scontrol1)
                    {
                        ForEach(scontrol1.Panel1, callback);
                        ForEach(scontrol1.Panel2, callback);
                    }
                    else if (control is SplitContainerControl scontrol)
                    {
                        ForEach(scontrol.Panel1, callback);
                        ForEach(scontrol.Panel2, callback);
                    }
                    else if (control is ContainerControl || control is XtraScrollableControl ||
                             control is ScrollableControl || control is GroupBox)
                    {
                        ForEach(control, callback);
                    }
                    else if (control is TabPane tabPane)
                    {
                        foreach (NavigationPageBase page in tabPane.Pages)
                        {
                            ForEach(page, callback);
                        }
                    }
                    else if (control is XtraTabControl tabControl1)
                    {
                        foreach (XtraTabPage tabPage in tabControl1.TabPages)
                        {
                            ForEach(tabPage, callback);
                        }
                    }
                    else if (control is TabControl tabControl)
                    {
                        foreach (TabPage tabPage in tabControl.TabPages)
                        {
                            ForEach(tabPage, callback);
                        }
                    }
                }
            }
            else if (container is BaseLayoutItem containerLayoutItem)
            {
                if (containerLayoutItem is LayoutControlItem layoutItem)
                {
                    callback?.Invoke(layoutItem.Control);
                }
                else if (containerLayoutItem is LayoutControlGroup controlGroup)
                {
                    foreach (BaseLayoutItem control in controlGroup.Items)
                    {
                        ForEach(control, callback);
                    }
                }
                else if (containerLayoutItem is TabbedControlGroup tabbedControlGroup)
                {
                    foreach (BaseLayoutItem layoutPage in tabbedControlGroup.TabPages)
                    {
                        ForEach(layoutPage, callback);
                    }
                }
            }
        }

        /// <summary>
        /// 清除容器中的所有控件及所有子孙控件的值
        /// </summary>
        /// <param name="container">容器控件</param>
        public virtual void ClearModel(object container)
        {
            ForEach(container, p =>
            {
                if (p is CheckedComboBoxEdit checkedComboBoxEdit)
                {
                    checkedComboBoxEdit.SetEditValue(null);
                }
                else if (p is BaseEdit baseEdit)
                {
                    baseEdit.EditValue = null;
                }
            });
        }

        /// <summary>
        /// 获取容器中的所有控件及所有子孙控件的值并绑定到指定对象中
        /// </summary>
        /// <param name="container">容器控件</param>
        /// <param name="model">绑定对象</param>
        public virtual object GetModel(object container, object model)
        {
            if (model == null) return null;
            ForEach(container, p =>
            {
                Control current = p as Control;
                if (current == null || current.Tag.IsEmpty()) return;
                GetFormDataCore(current, model);
            });
            return model;
        }

        /// <summary>
        /// 把指定对象中属性并绑定到容器中的所有控件及所有子孙控件上
        /// </summary>
        /// <param name="container">容器控件</param>
        /// <param name="model">绑定对象</param>
        public virtual object SetModel(object container, object model)
        {
            if (model == null) return null;
            ForEach(container, p =>
            {
                Control current = p as Control;
                if (current == null || current.Tag.IsEmpty()) return;
                SetFormDataCore(current, model);
            });
            return model;
        }

        /// <summary>
        /// 从编辑控件中取值并给Model对象赋值
        /// </summary>
        /// <param name="model">Model对象</param>
        /// <param name="modelPropertyName">Model对象属性名称</param>
        /// <param name="baseEdit">编辑控件</param>
        /// <param name="baseEditPropertyName">编辑控件属性名称</param>
        public virtual void SetModelFromBaseEdit(object model, string modelPropertyName, object baseEdit, string baseEditPropertyName)
        {
            var baseEditValue = ObjectHelper.GetObjectProperty(baseEdit, baseEditPropertyName);
            if (model is IDictionary dic)
            {
                dic[modelPropertyName] = baseEditValue;
            }
            else
            {
                ObjectHelper.SetObjectProperty(model, modelPropertyName, baseEditValue);
            }
        }

        /// <summary>
        /// 从Model对象取值并给编辑控件赋值
        /// </summary>
        /// <param name="baseEdit">编辑控件</param>
        /// <param name="baseEditPropertyName">编辑控件属性名称</param>
        /// <param name="model">Model对象</param>
        /// <param name="modelPropertyName">Model对象属性名称</param>
        public virtual void SetBaseEditFromModel(object baseEdit, string baseEditPropertyName, object model, string modelPropertyName)
        {
            object modelValue;
            if (model is IDictionary dic && dic.Contains(modelPropertyName))
            {
                modelValue = dic[modelPropertyName];
            }
            else
            {
                modelValue = ObjectHelper.GetObjectProperty(model, modelPropertyName);
            }

            if (baseEdit is CheckedComboBoxEdit edit)
            {
                edit.SetEditValue(modelValue);
            }
            else
            {
                ObjectHelper.SetObjectProperty(baseEdit, baseEditPropertyName, modelValue);
            }
        }

        /// <summary>
        /// 异步绑定表单数据
        /// </summary>
        /// <param name="container">容器控件</param>
        /// <returns>等待的任务</returns>
        public virtual async Task BindingDataAsync(object container)
        {
            if (container == null) throw new ArgumentNullException(nameof(container));
            var taskList = new List<Task>();
            ForEach(container, p =>
            {
                if (p is IAsyncBindingData current)
                {
                    var task = current.BindingDataAsync();
                    taskList.Add(task);
                }
            });
            await Task.WhenAll(taskList.ToArray());
        }

        /// <summary>
        /// 异步绑定控件EditValue
        /// </summary>
        /// <param name="container">容器控件</param>
        /// <returns>等待的任务</returns>
        public virtual async Task BindingEditValueAsync(object container)
        {
            if (container == null) throw new ArgumentNullException(nameof(container));
            var taskList = new List<Task>();
            ForEach(container, p =>
            {
                if (p is IAsyncBindingEditValue current)
                {
                    var task = current.BindingEditValueAsync();
                    taskList.Add(task);
                }
            });
            await Task.WhenAll(taskList.ToArray());
        }

        private void GetFormDataCore(Control control, object model)
        {
            var rules = GetRule(control.Tag.ToString());
            if (rules.IsEmpty()) return;
            foreach (var item in rules)
            {
                var modelPropertyName = item.Key;
                var baseEditPropertyName = item.Value;
                SetModelFromBaseEdit(model, modelPropertyName, control, baseEditPropertyName);
            }
        }

        private void SetFormDataCore(Control control, object model)
        {
            var rules = GetRule(control.Tag.ToString());
            if (rules.IsEmpty()) return;
            var rule = rules.FirstOrDefault(p => p.Value.Equals(R.ControlEditValueName));
            if (rule == null) return;

            var modelPropertyName = rule.Key;
            var baseEditPropertyName = rule.Value;
            SetBaseEditFromModel(control, baseEditPropertyName, model, modelPropertyName);
        }

        /// <summary>
        /// 解析Tag规则
        /// </summary>
        /// <param name="tagString"></param>
        /// <returns></returns>
        private List<KeyValue> GetRule(string tagString)
        {
            var rules = new List<KeyValue>();
            if (string.IsNullOrEmpty(tagString)) return rules;
            var sz = tagString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var item in sz)
            {
                if (string.IsNullOrEmpty(item)) continue;
                var index = item.IndexOf(':');
                var dataProName = item;
                var controlProName = R.ControlEditValueName;
                if (index > -1)
                {
                    dataProName = item.Substring(0, index);
                    controlProName = item.Substring(index + 1);
                }

                if (string.IsNullOrEmpty(dataProName) || string.IsNullOrEmpty(controlProName)) continue;
                if (rules.Any(p => p.Key.Equals(dataProName)))
                {
                    continue;
                }

                rules.Add(new KeyValue(dataProName, controlProName));
            }
            return rules;
        }
    }
}